﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using Tao.OpenGl;
using Tao.Platform.Windows;

using System.Runtime.InteropServices;
using rkShared;
using rkShared.BEncode;
using rkShared.Net;
using System.Collections;



namespace rkClient
{
    public partial class GameWindow : Form
    {
        /// <summary>
        /// This is the terrain where tanks move and fight
        /// </summary>
        private Terrain terrain;
        /// <summary>
        /// Holds information about the player
        /// </summary>
        private Player player;
        /// <summary>
        /// Holds connection information about clients
        /// </summary>
        private List<ClientInfo> _PlayerList;

        /// <summary>
        /// All the other players are kept in this dictionary
        /// </summary>
        private Dictionary<Guid, Player> _enemyPlayers = new Dictionary<Guid, Player>();

        /// <summary>
        /// Renderer class draws the game objects
        /// </summary>
        private Renderer renderer;
        /// <summary>
        /// Hires timer is used to calculate FPS and elapsed time between frames
        /// </summary>
        private O3L.HiResTimer hiresTimer;

        private Vector3 cameraPosition;
        private Vector3 cameraTarget;
        private Vector3 cameraUp;

        private Point cursorPos;

        private bool pivotSet = false;
        private float cameraPitch = 0.0f;

        /// <summary>
        /// Contains information about the game created 
        /// by the server.Seed game name etc.
        /// </summary>
        private GameInfo m_gameInfo = null;

        public bool AppActive = true;
        private bool showNormals = false;

        private float deltaTime = 0.0f;
        private float fps = 0.0f;
        /// <summary>
        /// The time it takes to spawn again when i die
        /// </summary>
        private float spawnTimer = 5.0f;

        private Vector3[] playerTerrainIntersectionRay = new Vector3[2];
        private Vector3[] mouseTerrainIntersectionRay = new Vector3[2];

        private GameClient m_gameClient;
        private ChatClient m_ChatClient;

        System.Media.SoundPlayer sp;

        public List<ClientInfo> PlayerList
        {
            get
            {
                if (_PlayerList == null) _PlayerList = new List<ClientInfo>();
                return _PlayerList;
            }
            set
            {
                _PlayerList = value;
            }
        }

        public Player Player
        {
            get { return player; }
        }

        public GameWindow(string playerName)
        {
            InitializeComponent();
            m_gameClient = new GameClient(AppGlobals.PlayerName, AppGlobals.ServerIP, 50005, 50006);
            m_ChatClient = new ChatClient(m_gameClient);
            //server'a kaydol
            m_gameClient.RegisterToServer();


            m_gameClient.PlayerDisconnected += new PlayerConnectionEventHandler(m_gameClient_PlayerDisconnected);
            m_ChatClient.ChatMessageReceived += new rkShared.Net.MessageReceivedEventHandler(m_ChatClient_ChatMessageReceived);
            m_gameClient.NewPlayerConnected += new PlayerConnectionEventHandler(m_gameClient_NewPlayerConnected);
            m_gameClient.GameMessageReceived += new MessageReceivedEventHandler(m_gameClient_GameMessageReceived);

            _PlayerList = new List<ClientInfo>();


            initOpenGL();

            //wait for seed from server
            while (m_gameInfo==null)
            {
                System.Threading.Thread.Sleep(1000);

            }
            
            //Arazi yarat
            terrain = new Terrain(128, m_gameInfo.Seed, 1000, 15, 25, 2);


            //Renderer objesini yarat
            renderer = new Renderer();

            //intersection rayleri yarat
            playerTerrainIntersectionRay[0] = new Vector3();
            playerTerrainIntersectionRay[1] = new Vector3();

            mouseTerrainIntersectionRay[0] = new Vector3();
            mouseTerrainIntersectionRay[1] = new Vector3();

            //Oyuncununu yerini ayarla
            player = new Player();
            player.Name = AppGlobals.PlayerName;
            player.ClientID = AppGlobals.ClientID;
            positionPlayer(randomVector3OnTerrain());
                       
            
            pbHitpoints.Value = player.Tank.Hitpoints * 10;
            scoresListView.Columns.Add("Players");
            scoresListView.Columns.Add("Scores");


            addPlayerToScoreBoard(player);


            //Cameranin yerini ayarla
            cameraTarget = new Vector3();
            cameraPosition = new Vector3();
            cameraUp = new Vector3(0.0f, 1.0f, 0.0f);
            positionCamera(true);

            //Timer başlat
            hiresTimer = new O3L.HiResTimer();
            hiresTimer.Start();

            //sesleri yükle
            sp = new System.Media.SoundPlayer("Data/fire.wav");

            //oyuncu objesini servera gonder
            NetMessage msg = new NetMessage();

            //get the client ID of the player
            msg.ClientID = AppGlobals.ClientID;
            msg.MessageType = MessageType.ClientEnteredGame;
            msg.Data = player;
            m_gameClient.SendTCPMessage(msg);


        }

        private void addPlayerToScoreBoard(Player newPlayer)
        {
            string[] text = new string[2];
            text[0] = newPlayer.Name;
            text[1] = newPlayer.KillCount.ToString();
            ListViewItem item = new ListViewItem(text);
            scoresListView.Items.Add(item);
            
        }
        private void initOpenGL()
        {
            OpenGLViewport.InitializeContexts();
            Gl.glClearColor(0.8f, 0.7f, 0.6f, 0.0f);
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glLoadIdentity();

            Gl.glEnable(Gl.GL_CULL_FACE);

            Gl.glEnable(Gl.GL_DEPTH_TEST);
            Gl.glDepthFunc(Gl.GL_LESS);

            Gl.glEnable(Gl.GL_LIGHT0);
            Gl.glEnable(Gl.GL_LIGHTING);
            Gl.glEnable(Gl.GL_COLOR_MATERIAL);

            //Enable texture mapping
            Gl.glEnable(Gl.GL_TEXTURE_2D);

            OpenGLViewport_Resize(this, null);
        }

        void m_gameClient_PlayerDisconnected(ClientInfo newPlayer)
        {
            _PlayerList.Remove(newPlayer);
            _enemyPlayers.Remove(newPlayer.ClientID);

            lblGameInfo.Invoke(new EventHandler(delegate
            {
                lblGameInfo.Text = newPlayer.NickName + " left game..";
            }));
            chatInputTextBox.Invoke(new EventHandler(delegate
            {
                chatTextBox.AppendText(newPlayer.NickName + " left game.. \r\n");
            }));
        }

        void m_gameClient_GameMessageReceived(ClientInfo client, NetMessage msg)
        {

            if (msg.MessageType == MessageType.Snapshot)
            {
                Snapshot s = msg.Data as Snapshot;
                foreach (Player p in s.Players)
                    _enemyPlayers[p.ClientID] = p;
            }
            if (msg.MessageType == MessageType.GameInfo)
            {
                GameInfo gi = msg.Data as GameInfo;
                m_gameInfo = gi;
            }

            if (msg.MessageType == MessageType.ClientEnteredGame)
            {
                _enemyPlayers[msg.ClientID] = msg.Data as Player;
                scoresListView.Invoke(new EventHandler(delegate 
                {
                    addPlayerToScoreBoard(_enemyPlayers[msg.ClientID]);
                }));                
            }
            if (msg.MessageType == MessageType.Move)
            {
                _enemyPlayers[msg.ClientID].Tank.Position = msg.Data as Vector3;
            }
            if (msg.MessageType == MessageType.Fire)
            {
                Vector3 destination = new Vector3();
                destination = msg.Data as Vector3;
                _enemyPlayers[msg.ClientID].Tank.fireBullet(destination, sp);

            }
            //someone hit me decrease my hitpoints and tell them im dead
            //if my hitpoints drops to 0 also tell the server who killed me
            //by sending their client id
            if (msg.MessageType == MessageType.Hit)
            {
                //if i have more than 0 hit points i can be hit
                if (player.Tank.Hitpoints > 0)
                {
                    player.Tank.Hitpoints--;
                    Guid _clientID = new Guid((msg.Data as BEncodedString).ValueStr);
                    
                    pbHitpoints.Invoke(new EventHandler(delegate
                    {
                        pbHitpoints.Value = player.Tank.Hitpoints * 10;
                    }));

                    player.Tank.Explosions.Add(new Billboard(player.Tank.Position, 20, 0, 10, true));
                    if (player.Tank.Hitpoints <= 0)
                    {
                        NetMessage newMsg = new NetMessage();
                        newMsg.Data = new BEncodedString(_clientID.ToString());
                        newMsg.MessageType = MessageType.Dead;
                        newMsg.ClientID = player.ClientID;
                        m_gameClient.SendUDPMessage(newMsg);


                        player.Tank.Explosions.Add(new Billboard(player.Tank.Position, 50, 0, 10,true));
                    }
                }
            }
            //someone died remove it from my list
            if (msg.MessageType == MessageType.Dead)
            {
                Guid _clientID = new Guid((msg.Data as BEncodedString).ValueStr);
                _enemyPlayers[_clientID].Tank.Explosions.Add(new Billboard(_enemyPlayers[_clientID].Tank.Position, 50, 0, 10,true));
                //dont remove it form my list
                //_enemyPlayers.Remove(_clientID);

            }
            //someone spawned again fuck! add them to my list so i can kill them again :)
            if (msg.MessageType == MessageType.Spawn)
            {
                _enemyPlayers[msg.ClientID] = msg.Data as Player;
            }
        }
        /// <summary>
        /// This is fired when someone registers to the server
        /// </summary>
        /// <param name="newPlayer"></param>
        void m_gameClient_NewPlayerConnected(rkShared.Net.ClientInfo newPlayer)
        {
            _PlayerList.Add(newPlayer);
            
            lblGameInfo.Invoke(new EventHandler(delegate
            {
                lblGameInfo.Text = newPlayer.NickName + " joined game..";
            }));
            chatInputTextBox.Invoke(new EventHandler(delegate
            {
                chatTextBox.AppendText(newPlayer.NickName + " joined game.. \r\n");
            }));
        }
        void m_ChatClient_ChatMessageReceived(rkShared.Net.ClientInfo client, rkShared.Net.NetMessage msg)
        {
            chatInputTextBox.Invoke(new EventHandler(delegate
                {
                    chatTextBox.AppendText((msg.Data as BEncodedString).ToUnicodeString() + "\r\n");
                }));
        }

        private void positionPlayer(Vector3 v)
        {
            player.Tank.Position.x = v.x;
            player.Tank.Position.z = v.z;
            player.Tank.Position.y = v.y;
            Vector3 intersectionPoint = new Vector3();

            updatePlayerTerrainIntersectionRay();
            intersectTerrainWithVerticalRay(playerTerrainIntersectionRay, out intersectionPoint);

            player.Tank.Position.y = intersectionPoint.y;
        }
        //positions the camera based on the players tank position
        //if startup is true the camera is placed behind the player in +z direction
        //if startup is false then this means camera can be anywhere behind the player
        //at a fixed height
        private void positionCamera(bool startup)
        {

            cameraTarget.Copy(player.Tank.Position);
            if (startup)
            {
                cameraPosition.x = player.Tank.Position.x;
                cameraPosition.z = player.Tank.Position.z + 150.0f;
            }

            cameraPosition.y = player.Tank.Position.y + 100.0f;

        }
        //Intersects the terrain with a ray
        private bool intersectTerrainWithRay(Vector3[] ray, out Vector3 foundPoint, Vector3 tankPos)
        {
            int cache = 0;
            int cache2 = 0;
            bool pointFound = false;
            float newDistance = 0.0f;
            float testDistance = 1000000.0f;

            //used for collision detection
            Vector3[] vPoly = new Vector3[3];
            vPoly[0] = new Vector3();
            vPoly[1] = new Vector3();
            vPoly[2] = new Vector3();
            //temporary intersection point
            Vector3 tmpInt = new Vector3();
            foundPoint = tmpInt;
            Vector3[] vertices = terrain.Vertices;
            int[] indices = terrain.Indices;

            int size = terrain.Size;
            int startX = (int)(tankPos.x / 10.0f) - 30;
            int startZ = (int)(-tankPos.z / 10.0f) - 30;



            for (int i = startZ; i < startZ + 60; ++i)
            {
                if (i < 0 || i > size - 2)
                    continue;
                cache2 = i * size;
                for (int k = startX; k < startX + 60; ++k)
                {
                    if (k < 0 || k > size - 2)
                        continue;
                    cache = k + cache2;

                    vPoly[0] = vertices[cache + size];
                    vPoly[1] = vertices[cache];
                    vPoly[2] = vertices[cache + size + 1];

                    if (UMath.IntersectedPolygon(vPoly, ray, 3, out tmpInt) == true)
                    {
                        if ((newDistance = UMath.Distance(tmpInt, tankPos)) < testDistance)
                        {
                            foundPoint = tmpInt;
                            pointFound = true;
                            testDistance = newDistance;
                        }
                    }

                    vPoly[0] = vertices[cache + size + 1];
                    vPoly[1] = vertices[cache];
                    vPoly[2] = vertices[cache + 1];

                    if (UMath.IntersectedPolygon(vPoly, ray, 3, out tmpInt) == true)
                    {
                        if ((newDistance = UMath.Distance(tmpInt, tankPos)) < testDistance)
                        {
                            foundPoint = tmpInt;
                            pointFound = true;
                            testDistance = newDistance;
                        }
                    }
                }
            }

            return pointFound;
        }

        //Intersects the terrain with a vertical ray on +y/-y
        private bool intersectTerrainWithVerticalRay(Vector3[] ray, out Vector3 foundPoint)
        {
            int cache = 0;
            int cache2 = 0;
            bool pointFound = false;

            //used for collision detection
            Vector3[] vPoly = new Vector3[3];
            vPoly[0] = new Vector3();
            vPoly[1] = new Vector3();
            vPoly[2] = new Vector3();
            //temporary intersection point
            Vector3 tmpInt = new Vector3();
            foundPoint = tmpInt;
            Vector3[] vertices = terrain.Vertices;
            int[] indices = terrain.Indices;

            int size = terrain.Size;
            int startX = (int)(ray[0].x / 10.0f) - 1;
            int startZ = (int)(-ray[0].z / 10.0f) - 1;


            for (int i = startZ; i < startZ + 3; ++i)
            {
                if (i < 0 || i > size - 2)
                    continue;
                cache2 = i * size;
                for (int k = startX; k < startX + 3; ++k)
                {
                    if (k < 0 || k > size - 2)
                        continue;
                    cache = k + cache2;

                    vPoly[0] = vertices[cache + size];
                    vPoly[1] = vertices[cache];
                    vPoly[2] = vertices[cache + size + 1];

                    if (UMath.IntersectedPolygon(vPoly, ray, 3, out tmpInt) == true)
                    {
                        foundPoint = tmpInt;
                        pointFound = true;
                    }

                    vPoly[0] = vertices[cache + size + 1];
                    vPoly[1] = vertices[cache];
                    vPoly[2] = vertices[cache + 1];

                    if (UMath.IntersectedPolygon(vPoly, ray, 3, out tmpInt) == true)
                    {
                        foundPoint = tmpInt;
                        pointFound = true;
                    }
                }
            }

            return pointFound;
        }
        /// <summary>
        /// Oyunun ana döngüsü
        /// </summary>
        public void MainLoop()
        {
            //Cizilen iki kare arasında gecen zaman deltaTime değişkeninde tutuluyor
            deltaTime = (float)hiresTimer.GetElapsedTime();
            fps = hiresTimer.CalculateFPS();



            //kullanıcıdan komut al
            checkInput();


            foreach (Player p in _enemyPlayers.Values)
                p.Tank.update(deltaTime);

            player.Tank.update(deltaTime);

            checkBulletCollisions();

            //check spawn spawnTimer
            if (player.Tank.Hitpoints <= 0)
            {
                spawnTimer -= deltaTime;
                if (spawnTimer <= 0)
                {
                    spawnTimer = 5.0f;
                    player.Tank.Hitpoints = 20;
                    pbHitpoints.Value = player.Tank.Hitpoints * 10;

                    positionPlayer(randomVector3OnTerrain());
                    positionCamera(true);

                    NetMessage msg = new NetMessage();
                    msg.ClientID = player.ClientID;
                    msg.Data = player;
                    msg.MessageType = MessageType.Spawn;
                    m_gameClient.SendUDPMessage(msg);
                }

            }


            //opengl viewportunu kendini tekrar cizmeye zorla
            OpenGLViewport.Draw();
        }
        public Vector3 randomVector3OnTerrain()
        {
            Random rnd = new Random();
            Vector3 v = new Vector3();

            float fx = (float)rnd.NextDouble() * 1280.0f;
            float fz = -(float)rnd.NextDouble() * 1280.0f;

            v.x = fx;
            v.z = fz;
            v.y = 1000.0f;
            return v;
        }
        public void checkBulletCollisions()
        {
            //check if a bullet of another tank hit this tank
            if (_enemyPlayers != null)
            {
                //tum dusmanlari tek tek dolan
                foreach (Player p in _enemyPlayers.Values)
                {
                    ArrayList bulletsToRemove = new ArrayList();
                    //benim her mermim icin
                    foreach (Bullet b in player.Tank.bullets)
                    {
                        //benim mermim ile dusman arasindaki mesafeyi bul
                        float distance = UMath.Distance(b.Position, p.Tank.Position);
                        //5 is the bounding sphere radius of tank
                        //1.5 is the bounding sphere radius of bullet
                        //These are hardcoded they should be variables later on :>
                        if (distance < 5.0f + 1.5f)
                        {
                            //servera vurualan main id sini yolla
                            NetMessage msg = new NetMessage();
                            msg.Data = new BEncodedString(p.ClientID.ToString());
                            msg.MessageType = MessageType.Hit;

                            m_gameClient.SendUDPMessage(msg);

                            //bullet yerinde bir patlama yarat
                            player.Tank.Explosions.Add(new Billboard(b.Position, 20, 0, 10,true));
                            //vuran mermiyi yok et

                            //player.Tank.bullets.Remove(b);    causes excception...cannot remove an item from a collection while iterating through foreach loop
                            bulletsToRemove.Add(b);
                        }
                    }
                    foreach (Bullet b in bulletsToRemove)
                        player.Tank.bullets.Remove(b);
                }
            }

        }
        //Klavyeden basılan tuşları kontrol et 
        //basılan tuşlara göre gerekli metodları cağır
        public void checkInput()
        {
            if (chatInputTextBox.Focused)
                return;
            bool playerMoved = false;
            cursorPos = PointToClient(Cursor.Position);

            float speed = 70.0f;
            int size = (int)(terrain.Size * 10.0f);
            bool recalculateCameraHeight = false;

            if (player.Tank.Hitpoints > 0)
            {

                //move player tank forward in look direction
                if (WinInput.IsKeyDown(Keys.W))
                {
                    Vector3 view = cameraTarget - cameraPosition;
                    view.y = 0.0f;
                    UMath.Normalize(ref view, view);
                    player.Tank.Position += view * deltaTime * speed;
                    cameraPosition += view * deltaTime * speed;
                    //keep player and camera inside map
                    checkBounds();

                    recalculateCameraHeight = true;
                    playerMoved = true;

                }
                //move player tank backwards in look direction
                if (WinInput.IsKeyDown(Keys.S))
                {

                    Vector3 view = cameraTarget - cameraPosition;
                    view.y = 0.0f;
                    UMath.Normalize(ref view, view);
                    player.Tank.Position -= view * deltaTime * speed;
                    cameraPosition -= view * deltaTime * speed;
                    //keep player and camera inside map
                    checkBounds();
                    recalculateCameraHeight = true;
                    playerMoved = true;
                }
                if (WinInput.IsKeyDown(Keys.D))
                {
                    Vector3 view = cameraTarget - cameraPosition;
                    Vector3 cross = new Vector3();
                    UMath.Normalize(ref view, view);
                    UMath.CrossProduct(cross, view, cameraUp);

                    UMath.Normalize(ref cross, cross);
                    player.Tank.Position += cross * deltaTime * speed;
                    cameraPosition += cross * deltaTime * speed;
                    //keep player and camera inside map
                    checkBounds();
                    recalculateCameraHeight = true;
                    playerMoved = true;
                }
                if (WinInput.IsKeyDown(Keys.A))
                {
                    Vector3 view = cameraTarget - cameraPosition;
                    Vector3 cross = new Vector3();
                    UMath.Normalize(ref view, view);
                    UMath.CrossProduct(cross, view, cameraUp);

                    UMath.Normalize(ref cross, cross);
                    player.Tank.Position -= cross * deltaTime * speed;
                    cameraPosition -= cross * deltaTime * speed;
                    //keep player and camera inside map
                    checkBounds();
                    recalculateCameraHeight = true;
                    playerMoved = true;
                }
            }
            //Rotate camera around player tank
            if (WinInput.IsKeyDown(Keys.Q))
            {
                UMath.RotateVector(deltaTime * 2.0f, ref cameraPosition, player.Tank.Position, 0.0f, 1.0f, 0.0f);

            }
            //rotate camera around player tank
            if (WinInput.IsKeyDown(Keys.E))
            {
                UMath.RotateVector(-deltaTime * 2.0f, ref cameraPosition, player.Tank.Position, 0.0f, 1.0f, 0.0f);
            }

                       
            //camera rotation with mouse right button
            if (pivotSet)
            {
                Point center = cursorPos;
                center.X = OpenGLViewport.Width / 2;
                center.Y = OpenGLViewport.Height / 2;

                Point difference = cursorPos;
                difference.X = center.X - cursorPos.X;
                difference.Y = center.Y - cursorPos.Y;

                int cameraTurnSpeed = 100;
                if (difference.X > 0)
                    difference.X = cameraTurnSpeed;
                if (difference.X < 0)
                    difference.X = -cameraTurnSpeed;



                if (difference.Y > 0)
                    difference.Y = cameraTurnSpeed / 2;
                if (difference.Y < 0)
                    difference.Y = -cameraTurnSpeed / 2;

                UMath.RotateVector((float)(difference.X * deltaTime * Math.PI / 180.0f), ref cameraPosition, player.Tank.Position, 0.0f, 1.0f, 0.0f);

                Vector3 view = cameraTarget - cameraPosition;
                Vector3 cross = new Vector3();
                UMath.Normalize(ref view, view);
                UMath.CrossProduct(cross, view, cameraUp);

                UMath.Normalize(ref cross, cross);
                cameraPitch += (float)(-difference.Y);
                if (cameraPitch > 1000.0f && difference.Y < 0)
                {
                    cameraPitch = 1000.0f;
                    difference.Y = 0;
                }
                else if (cameraPitch < -500.0f && difference.Y > 0)
                {
                    cameraPitch = -500.0f;
                    difference.Y = 0;
                }

                UMath.RotateVector((float)(difference.Y * deltaTime * Math.PI / 180.0f), ref cameraPosition, player.Tank.Position, cross.x, cross.y, cross.z);
            }

            if (recalculateCameraHeight)
                calculateCameraHeight();

            calculateCanonRotation();

            if (WinInput.IsKeyDown(Keys.F))
            {
                Gl.glPolygonMode(Gl.GL_FRONT, Gl.GL_LINE);
            }
            if (WinInput.IsKeyDown(Keys.G))
            {
                Gl.glPolygonMode(Gl.GL_FRONT, Gl.GL_FILL);
            }


            //send move message if player moved
            if (playerMoved)
            {
                NetMessage msg = new NetMessage();
                msg.Data = player.Tank.Position;
                msg.MessageType = MessageType.Move;
                m_gameClient.SendUDPMessage(msg);
            }
        }

        //keeps the camera and the player inside the game map
        private void checkBounds()
        {
            float delta = 0.0f;
            if (player.Tank.Position.x < 50)
            {
                delta = 50.0f - player.Tank.Position.x;
                player.Tank.Position.x += delta;
                cameraPosition.x += delta;
            }
            if (player.Tank.Position.x > 1230)
            {
                delta = player.Tank.Position.x - 1230;
                player.Tank.Position.x -= delta;
                cameraPosition.x -= delta;

            }
            if (player.Tank.Position.z > -50)
            {
                delta = -50 - player.Tank.Position.z;
                player.Tank.Position.z += delta;
                cameraPosition.z += delta;
            }
            if (player.Tank.Position.z < -1230)
            {
                delta = player.Tank.Position.z - (-1230);
                player.Tank.Position.z -= delta;
                cameraPosition.z -= delta;
            }
        }

        //Calculates where the canon of the tank should look
        //based on the position of the cursor over the viewport and 
        //the rotation of the tank itself
        private void calculateCanonRotation()
        {
            Point cursor = PointToClient(MousePosition);
            Point center = new Point(OpenGLViewport.Width / 2, OpenGLViewport.Height / 2);

            Point view = new Point();
            view.X = center.X - cursor.X;
            view.Y = center.Y - cursor.Y;
            view.Y = -view.Y;
            Vector3 refrenceView = new Vector3();
            refrenceView = (cameraTarget - cameraPosition);


            Point right = new Point(0, -1);


            UMath.Normalize(ref refrenceView, cameraTarget - cameraPosition);
            refrenceView.y = 0.0f;
            UMath.Normalize(ref refrenceView, refrenceView);
            double angle3d = UMath.AngleBetweenVectors(refrenceView, new Vector3(0.0f, 0.0f, -1.0f));

            float dotProduct = view.X * right.X + view.Y * right.Y;
            float vectorMagnitude = (float)(Math.Sqrt((view.X * view.X) + (view.Y * view.Y)) *
                                            Math.Sqrt((right.X * right.X) + (right.Y * right.Y)));
            if (vectorMagnitude == 0)
                return;

            double mouseAngle = Math.Acos(dotProduct / vectorMagnitude);

            //check the number
            if (Double.IsNaN(mouseAngle))
                return;
            float mouseAngleDegree = (float)(mouseAngle * 180 / Math.PI);
            float angle3dDegree = (float)(angle3d * 180 / Math.PI);

            //if mouse cursor is on the left side of the screen degrees should range
            //from 180 to 360
            if (cursor.X < center.X)
                mouseAngleDegree = 360 - mouseAngleDegree;
            //if reference view is lookin in -x direction degreese should range 
            //from 180 to 360
            if (refrenceView.x < 0.0f)
                angle3dDegree = 360 - angle3dDegree;

            //finally set the rotation in degrees
            player.Tank.CanonRotation = mouseAngleDegree + angle3dDegree;

        }
        //Kameranın arazi üstünde nerde olması gerektigini bulan fonksiyon
        private void calculateCameraHeight()
        {
            Vector3 intersectionPoint = new Vector3();
            updatePlayerTerrainIntersectionRay();

            //eger kesisim noktasi bulunursa devam et
            if (intersectTerrainWithVerticalRay(playerTerrainIntersectionRay, out intersectionPoint) == true)
            {
                float difference = cameraPosition.y - player.Tank.Position.y;
                player.Tank.Position = intersectionPoint;
                cameraPosition.y = intersectionPoint.y + difference;
                cameraTarget.Copy(player.Tank.Position);
                //positionCamera(false);
            }
        }
        //OpenGL penceresini cizimini yapan fonksiyon
        private void OpenGLViewport_Paint(object sender, PaintEventArgs e)
        {
            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);

            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            Gl.glLoadIdentity();

            Glu.gluLookAt(cameraPosition.x, cameraPosition.y, cameraPosition.z,
                          cameraTarget.x, cameraTarget.y, cameraTarget.z,
                          cameraUp.x, cameraUp.y, cameraUp.z);

            positionLights();


            Gl.glColor3f(1.0f, 1.0f, 1.0f);
            renderer.drawTerrain(terrain, showNormals);

            //drawn each of the enemy tanks
            foreach (Player p in _enemyPlayers.Values)
                renderer.drawTank(p.Tank);
            //draw this clients tank
            renderer.drawTank(player.Tank);

            Gl.glFlush();
        }
        //Isık yerleştir
        private void positionLights()
        {
            float[] position = new float[4];
            float[] diffuse = new float[4];
            float[] ambient = new float[4];

            position[0] = -1.0f;
            position[1] = 1.0f;
            position[2] = 1.0f;
            position[3] = 0.0f;

            diffuse[0] = diffuse[1] = diffuse[2] = 1.0f;
            diffuse[3] = 1.0f;
            ambient[0] = ambient[1] = ambient[2] = 0.0f;
            ambient[3] = 1.0f;

            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, position);
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, diffuse);
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, ambient);

        }
        //OpenGL penceresi  boyut değiştirirse 
        private void OpenGLViewport_Resize(object sender, EventArgs e)
        {
            if (OpenGLViewport.Height == 0)
                OpenGLViewport.Height = 1;

            float ratio = (float)OpenGLViewport.Width / OpenGLViewport.Height;

            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glLoadIdentity();


            Gl.glViewport(0, 0, OpenGLViewport.Width, OpenGLViewport.Height);

            Glu.gluPerspective(45, ratio, 0.1, 10000);
            Gl.glMatrixMode(Gl.GL_MODELVIEW);

        }

        void setOrthographicProjection()
        {
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glPushMatrix();
            Gl.glLoadIdentity();
            Gl.glOrtho(0, OpenGLViewport.Width, 0, OpenGLViewport.Height, 0, 1);
            Gl.glMatrixMode(Gl.GL_MODELVIEW);
        }

        void resetPerspectiveProjection()
        {
            // set the current matrix to GL_PROJECTION
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            // restore previous settings
            Gl.glPopMatrix();
            // get back to GL_MODELVIEW matrix
            Gl.glMatrixMode(Gl.GL_MODELVIEW);
        }
        //Update the ray that passes through the camera and goes directly down
        //to the terrain
        void updatePlayerTerrainIntersectionRay()
        {
            playerTerrainIntersectionRay[0].Copy(player.Tank.Position);
            playerTerrainIntersectionRay[0].y += 5000.0f;
            playerTerrainIntersectionRay[1].Copy(player.Tank.Position);
            playerTerrainIntersectionRay[1].y -= 5000.0f;
        }
        //update the ray that passes through the camera and the cursors 3d position
        void updateMouseTerrainIntersectionRay()
        {
            mouseTerrainIntersectionRay[0].Copy(cameraPosition);
            Point p = OpenGLViewport.PointToClient(MousePosition);

            mouseTerrainIntersectionRay[1].Copy(get3dMousePosition(p));
        }
        //Find the cursors 3d position in the world by unprojecting it
        Vector3 get3dMousePosition(Point cursor)
        {
            Vector3 mouse3d = new Vector3();

            //these can be members of OpenGLWindow
            Double[] model_view = new Double[16];
            Double dx, dy, dz;
            Double[] projection = new Double[16];
            int[] viewport = new int[4];


            Gl.glGetDoublev(Gl.GL_MODELVIEW_MATRIX, model_view);
            Gl.glGetDoublev(Gl.GL_PROJECTION_MATRIX, projection);
            Gl.glGetIntegerv(Gl.GL_VIEWPORT, viewport);

            //cursor.X = cursor.X - (ClientRectangle.Right - viewport[2]);
            //cursor.Y = cursor.Y - (ClientRectangle.Bottom - viewport[3]);

            int realy = viewport[3] - (int)cursor.Y - 1;

            // get 3D coordinates based on window coordinates	
            Glu.gluUnProject(cursor.X, realy, 1.0,
                    model_view, projection, viewport,
                    out dx, out dy, out dz);

            mouse3d.x = (float)dx;
            mouse3d.y = (float)dy;
            mouse3d.z = (float)dz;

            return mouse3d;

        }
        public void writeToChatTextBox(String text)
        {
            chatTextBox.AppendText(text + "\r\n");

        }
        //oyuncu tarafından gönderilen mesajlar
        private void chatSendButton_Click(object sender, EventArgs e)
        {
            if (chatInputTextBox.Text.Length != 0)
            {
                string _message = AppGlobals.PlayerName + " : " + chatInputTextBox.Text;
                chatTextBox.AppendText(_message + "\r\n");
                //kullanıcı tarafından yazılan mesajın servera gönderilmesi işlemi
                m_ChatClient.SendMessageToServer(new BEncodedString(_message, true));
                chatInputTextBox.Clear();
                OpenGLViewport.Focus();
            }
        }

        private void chatInputTextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)Keys.Enter && chatInputTextBox.Text.Length != 0)
            {
                chatTextBox.AppendText(chatInputTextBox.Text + "\r\n");
                chatInputTextBox.Clear();
                e.Handled = true;
            }
        }

        private void OpenGLViewport_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.N)
            {
                showNormals = !showNormals;
            }
            if (e.KeyCode == Keys.R)
            {
                chatInputTextBox.Focus();
            }
        }

        private void GameWindow_FormClosed(object sender, FormClosedEventArgs e)
        {
            //Delete textures explicitly before the OpenGL context gets lost			
            terrain.GrassTexture.Destroy();
            terrain.RockTexture.Destroy();
            renderer.CleanUp();
            m_gameClient.Dispose();

        }
        private void OpenGLViewport_MouseDown(object sender, MouseEventArgs e)
        {
            //fire with left mouse button
            if (e.Button == MouseButtons.Left)
            {
                //cant fire if tank is dead
                if (player.Tank.Hitpoints <= 0)
                    return;
                Vector3 destination = new Vector3();
                Vector3 destinationReal = new Vector3();
                
                updateMouseTerrainIntersectionRay();
                //intersectTerrainWithVerticalRay(mouseTerrainIntersectionRay,out destination);
                if (intersectTerrainWithRay(mouseTerrainIntersectionRay, out destination, player.Tank.Position))
                {
                    mouseTerrainIntersectionRay[0].Copy(player.Tank.Position);
                    mouseTerrainIntersectionRay[0].y += 5;
                    mouseTerrainIntersectionRay[1].Copy(destination);
                    intersectTerrainWithRay(mouseTerrainIntersectionRay, out destinationReal, player.Tank.Position);
                    
                    if (UMath.Distance(destination, player.Tank.Position) < UMath.Distance(destinationReal, player.Tank.Position))
                        destinationReal.Copy(destination);
                    //cant fire too close to myself
                    if(UMath.Distance(destinationReal,player.Tank.Position ) < 40.0f)
                        return;

                    player.Tank.fireBullet(destinationReal, sp);
                    NetMessage msg = new NetMessage();
                    msg.ClientID = AppGlobals.ClientID;
                    msg.Data = destinationReal;
                    msg.MessageType = MessageType.Fire;
                    m_gameClient.SendUDPMessage(msg);
                }
            }
            //rotate camera with right mouse button
            if (e.Button == MouseButtons.Right)
            {
                pivotSet = true;
            }
        }
        private void OpenGLViewport_MouseUp(object sender, MouseEventArgs e)
        {
            //rotate camera with right mouse button
            if (e.Button == MouseButtons.Right)
            {
                pivotSet = false;
            }
        }

        private void GameWindow_Activated(object sender, EventArgs e)
        {
            AppActive = true;
        }

        private void GameWindow_Deactivate(object sender, EventArgs e)
        {
            AppActive = false;
        }

    }
    public class WinInput
    {
        public static bool IsKeyDown(Keys k)
        {
            return (GetAsyncKeyState((int)k) & 0x8000) != 0;
        }


        // Imported Win32-func

        [DllImport("User32.dll")]
        private static extern short GetAsyncKeyState(int keyCode);
    }
}

